import * as url from 'url';
import * as ws from 'ws';
import * as events from 'events';
import * as express from "express";
import * as expressSession from "express-session";

import * as log from '../log/log';
import * as wsEvent from './event-websocket';
import * as protobuf from '../protobuf/protobuf-msg';

/**
 * websocket的服务对象，可以建立监听服务
 * @author yangxiao
 */
export class ServerWebSocket extends events.EventEmitter{
    
    private _wsServer:ws.Server;
    private _sessionParser:any;
    private _port:number;
    private _clientList:ws[]=[];
    private _isStop = false;

    
    /**
     * 最大连接数
     * @type {number}
     */
    public maxConnection:number = 3000;
    
    //允许连接进入的客户列表
    private _domain = [ 

    ]

    constructor(port?:number){
        super();
        if(port){
            this.createServer(port);
        }
    }

    /**
     * 设置可以被接入的客户列表（域名|IP形式，不包含http://）,设置空数组代表没有任何限制,包括独立客户端的连接
     * 示例：
     * setDomain['127.0.0.1','localhost:8080']
     */
    public setDomain(list:string[]){
        this._domain = list;
    }

    /**
     * 建立监听服务
     */
    createServer(port:number){
        this._port = port;
        let app = express();
        this._sessionParser = expressSession({
            secret:"secret",
            resave: true,
            saveUninitialized: true
        });
        
        app.use(this._sessionParser);
        //let httpServer =app.listen(8080,"0.0.0.0", function () { console.log('Listening on http' , httpServer.address().address, httpServer.address().port) });
        this._wsServer = ws.createServer({port:port});
        this._wsServer.on('connection', results=>this.connection(results));
        this._wsServer.on("headers", results=>this.headers(results));
        this._wsServer.on("error", results=>this.error(results));
        console.log('Listening on websocket' , port);
    }

    
    
    /**
     * 停止服务
     * 
     * @param {string} [msg='server close']
     * 
     * @memberOf ServerWebSocket
     */
    public stop(msg = 'server close'){
        this._isStop = true;
        this._wsServer.clients.forEach(function each(client) {
            client.close(1000,msg);
        });
    }
    
    
    /**
     * 获得已经连接的所有客户
     * 
     * @returns {ws[]}
     * 
     * @memberOf ServerWebSocket
     */
    public getClients():ws[]{
        if(this._wsServer){
            return this._wsServer.clients;
        }
        return [];
    }

    
    /**
     * 广播
     * 
     * @param {*} data
     * 
     * @memberOf ServerWebSocket
     */
    public broadcast(data:any){
        if(this._wsServer){
            this._wsServer.clients.forEach(function each(client) {
                client.send(data);
            });
        }else{
            console.log('server-websocket-broadcast wsServer is not connection');
        }
    }

    
    /**
     * 根据sessionID获得一个客户端连接
     * 
     * @param {any} sessionID
     * @returns {ws}
     * 
     * @memberOf ServerWebSocket
     */
    public getClient(sessionID):ws{
        return this._clientList[sessionID];
    }
    
    private addClient(client){
        let sessionID = (client.upgradeReq as any).sessionID;
        if(this._clientList[sessionID]){
            return
        }
        this._clientList[sessionID]=client;
    }
    private removeClient(sessionID,client:ws){
        client.removeAllListeners();
        delete this._clientList[sessionID];
    }



    private connection(client:ws){
        if(this._isStop){
            client.close(1000,'server close');
            return;
        }
        // http://stackoverflow.com/a/16395220/151312)
        let clientUrl = client.upgradeReq.headers.originl;
        if(this._domain.length>0){
            if( typeof clientUrl==="undefined"){
                client.close(1000,'close , server-websocket-connection clientUrl is undefined');
                return;
            }
            if(clientUrl.indexOf("http")!==-1){
                clientUrl = clientUrl.substr(7);
            }else if(clientUrl.indexOf("https")!==-1){
                clientUrl  = clientUrl.substr(8);
            }
            if(this._domain.indexOf(clientUrl)===-1){
                client.close(1000,'close ,server-websocket-connection domain error');
                return;
            }
        }
        if(this.getClients().length>this.maxConnection){
                client.close(1000,'close ,server-websocket-connection maxConnection error');
                return;
        }
        this._sessionParser(client.upgradeReq as express.Request, {} as any , ()=>{
            
            let sessionId = (client.upgradeReq as any).sessionID;          
            this.addClient(client);
            console.log(this.getClients().length,'connection success ip ',client.upgradeReq.connection.remoteAddress,client.upgradeReq.connection.remotePort,sessionId);
            
        });
        
        client.on('message', results=>this.handleMessage(results,client));
        client.on("close",(code:number,message:string)=>this.handleClose(code,message,client));
        client.on("error",(err:Error)=>this.handleError(err,client)); 

        let clientIntervalid = setInterval(()=>{
            try{
                client.ping();
            }catch(e){
                clearInterval(clientIntervalid);
            }

        },3000);
    }

    private handleMessage(message,client:ws){
        
        try{
            this.emit(wsEvent.Event.clientMessage,message,client);
        }catch(e){
            client.close(1000,"error message Illegal format");
        }

    }
    private handleClose(code:number,message:string,client:ws){
        console.log("connection close code ip "+code,client.upgradeReq.connection.remoteAddress,message);
        this.emit(wsEvent.Event.clientClose,code,message);
        this.removeClient((client.upgradeReq as any).sessionID,client);
    }
    private handleError(err:Error,client:ws){
        log.getLogger(log.type.crashLog).error("server-websocket-handleError "+err.message);
        this.emit(wsEvent.Event.clientError,err);
        this.removeClient((client.upgradeReq as any).sessionID,client);
    }
    private headers(headers: string[]){
        this.emit(wsEvent.Event.headers,headers);
    }
    private error(err:Error){
        log.getLogger(log.type.crashLog).error("server-websocket-error "+err.message);
        this.emit(wsEvent.Event.error,err);
    }
}